package banksystem;

import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import java.util.HashMap;
import java.util.Hashtable;

/**
 *
 * @author Benjamin Oien
 */
public class Controller {
	public enum AccountAction {
		WITHDRAW, DEPOSIT, TRANSFER, TRANSFER2, VIEW, CLOSE
	}
	//Model/State variables
	AccountAction mAccountAction = null;	//tells SelectedAccount(Account) what to do with the passed in Account
	User mUser = null;						//Stores refrence to the logged-in User, User stores, creates and retrieves Accounts
	Account mAccount1 = null;				//stores refrence to the selected account
	Account mAccount2 = null;				//stores refrence to second selected account(for transfers)
	//JFrame refrences
	RootWindow mRootWindow = null;
	ActivitySelect mActivitySelect = null;
	FundManagementFrame mFundManagement = null;
	AccountSelectFrame mAccountSelect1 = null;
	AccountSelectFrame mAccountSelect2 = null; //added for account transfer
	WithdrawForm mWithdrawForm = null;
	DepositFrame mDepositForm = null;
	TransferFrame mTransferFrame = null;
	ViewFrame mViewFrame = null;
	CustomerRelationsFrame mCustomerRelations = null;
	CreateAccountFrame mCreateAccountFrame = null;
	ConfirmRemoveAccountFrame mConfirmRemoveAccountFrame = null;
	ModifyUserFrame mModifyUserFrame = null;
	InvestmentsFrame mInvestmentsFrame = null;
	CreateCDFrame mCreateCDFrame = null;
	CDSelectFrame mCDSelect = null;
	CDTransferFrame mCDTransferFrame = null;
	
	//Class which stores, creates and query's User objects
	Database mDatabase = null;

	public Controller() {
		mDatabase = new Database();
		
		mRootWindow = new RootWindow(this);
		mRootWindow.setVisible(true);
	}
	
	public Controller(Database database) {
		mDatabase = database;
		mRootWindow = new RootWindow(this);
		mRootWindow.setVisible(true);
	}
	
	public void CustomerRelations() {
		mCustomerRelations = new CustomerRelationsFrame(this);
		ChangeWindow(mActivitySelect, mCustomerRelations);
	}
	public void Investments() {
		mInvestmentsFrame = new InvestmentsFrame(this);
		ChangeWindow(mActivitySelect, mInvestmentsFrame);
	}
	public void SelectBuyCD() {
		if(mUser.getAccounts().isEmpty()) {
			JOptionPane.showMessageDialog(mInvestmentsFrame, "Bank account required to purchase a CD", "Info", JOptionPane.INFORMATION_MESSAGE);
		} else {
			mCreateCDFrame = new CreateCDFrame(this, this.mUser.getCDs());
			ChangeWindow(mInvestmentsFrame, mCreateCDFrame);
		}
	}
	
	// change view to account select for selecting which Bank Account to close
	public void SelectViewCD() {
		mAccountAction = AccountAction.VIEW;
		
		mCDSelect = new CDSelectFrame( this, mUser.getCDs() );
		ChangeWindow(mInvestmentsFrame, mCDSelect);
	}
		
	// change view to account select for selecting which Bank Account to close
	public void SelectSellCD() {
		mAccountAction = AccountAction.WITHDRAW;
		
		mCDSelect = new CDSelectFrame( this, mUser.getCDs() );
		ChangeWindow(mInvestmentsFrame, mCDSelect);
	}
	
	public void SelectOpenBankAccount() {
		mCreateAccountFrame = new CreateAccountFrame(this, this.mUser.getAccounts());
		ChangeWindow(mCustomerRelations, mCreateAccountFrame);
	}
	
	// change view to account select for selecting which Bank Account to close
	public void SelectCloseBankAccount() {
		mAccountAction = AccountAction.CLOSE;
		
		mAccountSelect1 = new AccountSelectFrame( this, mUser.getAccounts() );
		ChangeWindow(mCustomerRelations, mAccountSelect1);
	}
	
	public void CreateAccount(String accountName, AccountType type, double balance) {
		Account account = mUser.getAccount(mUser.addNewAccount());
		account.setName(accountName);
		account.setType(type);
		account.setBalance(balance);
		this.SaveAndRestart(this.mCreateAccountFrame);
	}
	
	public void CreateCD(String cdName, CDPackage pac, double balance) {
		 CertificateOfDeposit CD = mUser.getCD(mUser.addNewCD(balance, pac));
		 CD.setName(cdName);
		 this.SaveAndRestart(mCreateCDFrame);
	}
	
	//If a user with the username doesn't exist, creates a new User and stores to database
	//STUB for add Account: adds an Account to the user named "Checking Stub"
	public void Register(String userName, String pass) { 
		// Get a new user from the database (user is not automatically saved in database: mDatabase.saveUser() must be called to save)
		// If the user already exists, the database returns a null User
		User tempUser = mDatabase.createUser(userName, pass);	

		if(tempUser != null) {
			// Switch Windows to modify the user info.
			// Passes refrences to the
			mUser = tempUser;
			mModifyUserFrame = new ModifyUserFrame(this);
			ChangeWindow(mRootWindow, this.mModifyUserFrame);
			//STUB creates an account for every User registered.
			//----------------------------------------------------------------
			//Account account = tempUser.getAccount(tempUser.addNewAccountCustomID(1));
			//account.setName("Checking Stub");
			//account.setBalance(500);
			//Account account2 = tempUser.getAccount(tempUser.addNewAccountCustomID(2));
			//account2.setName("Rainy Day Stub");
			//account2.setBalance(350);
			//Account account3 = tempUser.getAccount(tempUser.addNewAccountCustomID(3));
			//account3.setName("Savings Stub");
			//account3.setBalance(5000);
			
			//----------------------------------------------------------------
			// endof STUB
			
		} else {
			JOptionPane.showMessageDialog(mRootWindow, "UserName Already Exists: " + userName, "Info", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	public void GoToUserInfo() {
		mModifyUserFrame = new ModifyUserFrame(this, mUser.getFirstName(),
													 mUser.getLastName(),
													 mUser.getAddress(),
													 mUser.getPhoneNumber());
		ChangeWindow(mCustomerRelations, this.mModifyUserFrame);
	}
	
	public void ModifyUserInfo(String fName, String lName, String address, String phoneNumber, boolean isEdit) {
		mUser.setFirstName(fName);
		mUser.setLastName(lName);
		mUser.setAddress(address);
		mUser.setPhoneNumber(phoneNumber);
		
		mDatabase.saveUser();

		//if it's coming from editing an existing user, save and return
		if(isEdit) {
			JOptionPane.showMessageDialog(mModifyUserFrame, "Updated User: " + mUser.getUserName(), "Info", JOptionPane.INFORMATION_MESSAGE);
			SaveAndRestart(mModifyUserFrame);
		} else { // if it's coming from registering a user, return to RootWindow
			JOptionPane.showMessageDialog(mModifyUserFrame, "Registered User: " + mUser.getUserName(), "Info", JOptionPane.INFORMATION_MESSAGE);
			CloseUserRegisterWindow();
		}
	}
	

	public void CancelUserModify(boolean isEdit) {
		//if it's coming from editing an existing user, save and return
		if(isEdit) {
			SaveAndRestart(mModifyUserFrame);
		} else { // if it's coming from registering a user, return to RootWindow
			CloseUserRegisterWindow();
		}
	}

	
	//switches window from mModifyUserFrame to RootWindow
	private void CloseUserRegisterWindow() {
		mUser = null;
		mRootWindow = new RootWindow(this);
		ChangeWindow(mModifyUserFrame, mRootWindow);
	}
	
	//If a User with username/password combo exists, 
	//store Uesr refrence in mUser and change window to ActivitySelect
	public void Login(String userName, String pass) {
		mUser = mDatabase.login(userName, pass);
		
		// if the database returned a User
		if(null != mUser) {
			// change window to ActivitySelect
			mActivitySelect = new ActivitySelect(this);
			ChangeWindow(mRootWindow, mActivitySelect);
		} else {
			JOptionPane.showMessageDialog(mRootWindow, "Username/Password wrong", "Info", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	//Changes window from ActivitySelect to FundManagementFrame
	public void Funds() {
		mFundManagement = new FundManagementFrame(this);
		ChangeWindow(mActivitySelect, mFundManagement);
	}
	
	//sets the action to be performed on the account(s) to WITHDRAW
	//changes window to AccountSelect
	public void SelectWithdraw() {
		// set the account action so that it is remembered for later
		mAccountAction = AccountAction.WITHDRAW;

		mAccountSelect1 = new AccountSelectFrame( this, mUser.getAccounts() );
		ChangeWindow(mFundManagement, mAccountSelect1);
	}
	
	public void SelectDeposit()
	{
		mAccountAction = AccountAction.DEPOSIT;
		
		mAccountSelect1 = new AccountSelectFrame( this, mUser.getAccounts() );
		ChangeWindow(mFundManagement, mAccountSelect1);
	}
	
	public void SelectTransfer()
	{
		mAccountAction = AccountAction.TRANSFER;
		
		mAccountSelect1 = new AccountSelectFrame( this, mUser.getAccounts() );
		ChangeWindow(mFundManagement, this.mAccountSelect1);
	}
	
	public void SelectView()
	{
		mAccountAction = AccountAction.VIEW;
		
		mAccountSelect1 = new AccountSelectFrame( this, mUser.getAccounts() );
		ChangeWindow(mFundManagement, this.mAccountSelect1);
	}
	
	//A refrence to the account argument is stored
	//window switches depending on AccountAction to be performed
	public void SelectedAccount(Account account) {
		switch(mAccountAction) {
			case DEPOSIT :
				mAccount1 = account;
				// continue to DepositForm
				mDepositForm = new DepositFrame(this, mAccount1);
				ChangeWindow(mAccountSelect1, mDepositForm);
				break;
			case WITHDRAW :
				mAccount1 = account;
				// continue to WithdrawForm
				mWithdrawForm = new WithdrawForm(this, mAccount1);
				ChangeWindow(mAccountSelect1, mWithdrawForm);
				break;
			case TRANSFER :
				// store first account
				mAccount1 = account;
				//remove first account from consideration -- Rob McCune
				HashMap<Long, Account> tmpHash = new HashMap<Long, Account>(this.mUser.getAccounts());//Uses copy constructor since remove is called -- Rob McCune
				tmpHash.remove(mAccount1.getID());
				this.mAccountSelect2 = new AccountSelectFrame(this, tmpHash);
				mAccountAction = AccountAction.TRANSFER2;
				ChangeWindow(mAccountSelect1, mAccountSelect2);
				break;
			case TRANSFER2 :
				// store second account
				mAccount2 = account;
				//If mAccout 1 is a CD, go to the CDTransfer instead
				if(mAccount1.getClass().equals(CertificateOfDeposit.class)) {
					mCDTransferFrame = new CDTransferFrame(this,(CertificateOfDeposit) mAccount1, mAccount2);
					ChangeWindow(mAccountSelect2, mCDTransferFrame);
				} else {
					mTransferFrame = new TransferFrame(this, mAccount1, mAccount2);
					ChangeWindow(mAccountSelect2, mTransferFrame);
				}
				break;
			case VIEW :
				mAccount1 = account;
				this.mViewFrame = new ViewFrame(this, mAccount1);
				ChangeWindow(mAccountSelect1, mViewFrame);
				break;
			case CLOSE :
				mAccount1 = account;
				// promt user if sure
				mConfirmRemoveAccountFrame = new ConfirmRemoveAccountFrame(this, mAccount1);
				ChangeWindow(mAccountSelect1, mConfirmRemoveAccountFrame);

				break;
		}
	}
	
	//A refrence to the account argument is stored
	//window switches depending on AccountAction to be performed
	public void SelectedCD(Account CD) {
		switch(mAccountAction) {
//			case WITHDRAW :
//				mAccount1 = CD;
//				// continue to WithdrawForm
//				mWithdrawForm = new WithdrawForm(this, mAccount1);
//				ChangeWindow(mCDSelect, mWithdrawForm);
			case VIEW :
				mAccount1 = CD;
				this.mViewFrame = new ViewFrame(this, mAccount1);
				ChangeWindow(mCDSelect, mViewFrame);
				break;
//			case CLOSE :
//				mAccount1 = CD;
//				// promt user if sure
//				mConfirmRemoveAccountFrame = new ConfirmRemoveAccountFrame(this, mAccount1);
//				ChangeWindow(mCDSelect, mConfirmRemoveAccountFrame);
//				break;
			case WITHDRAW :
				// store first account
				mAccount1 = CD;
				//remove first account from consideration -- Rob McCune
				this.mAccountSelect2 = new AccountSelectFrame(this, this.mUser.getAccounts());
				mAccountAction = AccountAction.TRANSFER2;
				ChangeWindow(mCDSelect, mAccountSelect2);
				break;
			//case TRANSFER2 :		//gets caught by SelectedAccount
			//	// store second account
			//	mAccount2 = CD;
			//	mCDTransferFrame = new CDTransferFrame(this,(CertificateOfDeposit) mAccount1, mAccount2);
			//	ChangeWindow(mAccountSelect2, mCDTransferFrame);
			//	break;
		}
	}
	
	public void deleteAccount() {
		//TODO remove the account object from the DB
		if(mAccount1.getClass().equals(CertificateOfDeposit.class)) {
			mUser.removeCD(mAccount1.getID());
		} else {
			mUser.removeAccount(mAccount1.getID());
		}
		if(mConfirmRemoveAccountFrame != null) {
			SaveAndRestart(mConfirmRemoveAccountFrame);
		}
	}
	
	//withdraw is called on the account
	//if success, the program saves to database and starts back at ActivitySelect
	public void WithdrawFromAccount(Double amount) {
		boolean withdrawSuccess = mAccount1.Withdraw(amount);
		if(withdrawSuccess) {
			SaveAndRestart(mWithdrawForm);
		} else {
			JOptionPane.showMessageDialog(mWithdrawForm, "Withdraw unsuccessful", "Info", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	//withdraw is called on the account
	//if success, the program saves to database and starts back at ActivitySelect
	public void DepositToAccount(Double amount) {
		boolean depositSuccess = mAccount1.Deposit(amount);
		if(depositSuccess) {
			SaveAndRestart(mDepositForm);
		} else {
			JOptionPane.showMessageDialog(mDepositForm, "Deposit unsuccessful", "Info", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	//Withdraws amount from the first account
	//if success, deposits into second account then saves and switches window to ActivitySelect
	public void TransferBetweenAccounts(double amount) {
		TransferTransaction transfer = new TransferTransaction(amount, mAccount1, mAccount2);
		boolean success = false;
		boolean isFromCD = false;
		
		if(mAccount1.getClass().equals(CertificateOfDeposit.class)) {
			isFromCD = true;
			CertificateOfDeposit tempCD = (CertificateOfDeposit) mAccount1;
			if(tempCD.isMatured()) {
				this.deleteAccount();
				success = true;
			} else {
				success = tempCD.Withdraw(amount, transfer);//new checks for success with both transactions
			}
			
		} else {
			success = mAccount1.Withdraw(amount, transfer);//new checks for success with both transactions
		}

		if(success) {
			success = mAccount2.Deposit(amount, transfer);
			if(success) {//both must complete successfully 
				if(isFromCD) {
					SaveAndRestart(mCDTransferFrame);
				} else {
					SaveAndRestart(mTransferFrame);
				}
			}else{
				mAccount1.getTransactionList().remove(transfer);//removes uncompleted transfer
				if(isFromCD) {
					JOptionPane.showMessageDialog(mTransferFrame, "Transfer unsuccessful", "Info", JOptionPane.INFORMATION_MESSAGE);
				} else {
					JOptionPane.showMessageDialog(mCDTransferFrame, "Transfer unsuccessful", "Info", JOptionPane.INFORMATION_MESSAGE);
				}
				
			}
		}else{
			if(isFromCD) {
				JOptionPane.showMessageDialog(mTransferFrame, "Transfer unsuccessful", "Info", JOptionPane.INFORMATION_MESSAGE);
			} else {
				JOptionPane.showMessageDialog(mCDTransferFrame, "Transfer unsuccessful", "Info", JOptionPane.INFORMATION_MESSAGE);
			}
		}
	}
	
	//Saves the database
	//nulls refrences to state variables except mUser
	//changes window ActivitySelect
	public void SaveAndRestart(JFrame window) {
		mDatabase.saveUser();
		
		mAccount1 = null;
		mAccount2 = null;
		mAccountAction = null; 
		
		mActivitySelect = new ActivitySelect(this);
		ChangeWindow(window, mActivitySelect);
	}
	
	
	public void Logout(JFrame window) {
		mDatabase.saveUser();
		
		mAccount1 = null;
		mAccount2 = null;
		mAccountAction = null; 
		mUser = null;
		
		mRootWindow = new RootWindow(this);
		ChangeWindow(window, mRootWindow);
	}
	
	//Disposes From JFrame and instatiates the to JFrame
	//Note: 'to' JFrame must be initialized before calling!
	private void ChangeWindow(JFrame from, JFrame to) {
		Point windowLocation = from.getLocationOnScreen();
		from.setVisible(false);
		to.setLocation(windowLocation);
		to.setVisible(true);
		from.dispose(); 
		from = null;
	}
	
	//Saves the database and exits the entire Program
	public void ExitProgram() {
		if(mDatabase != null) {
			mDatabase.saveUser();
			mDatabase.Dispose();
		}
		System.exit(0);
	}
}
